/**
 * 打印对象类型
 * 该方法将通过：Object.prototype.toString, typeof, constructor.name 三种方法依次打印出对象的类型
 * @param {*} obj 任意对象
 */
function toStringCall(obj) {
    const instanceStr = Object.prototype.toString.call(obj);
    const typeOfStr = typeof obj;
    let className = 'no name';
    if (obj !== null && obj !== undefined) className = obj.constructor.name;

    let strObj = '';
    try {
        strObj = JSON.stringify(obj);
    } catch (ex) {
        strObj = obj.toString();
    }

    console.log(`[toStringCall] ${strObj}`);
    console.log(`---- toString.call: ${instanceStr}`);
    console.log(`---- typeof: ${typeOfStr}`);
    console.log(`---- class: ${className}`);
}

/** 返回字符串形式的类型，以全小写的形式
 * null undefined number string symbol bigint
 * object date regexp set weakmap map
 * any custom class
 */

/**
 * 返回对象的最准确类型字符串，字符串为全小写
 * @param {*} obj
 * @returns 对象类型字符串
 */
function strClassName(obj) {
    if (obj === null) return 'null';
    if (obj === undefined) return 'undefined';

    let strClass = Object.prototype.toString.call(obj);
    strClass = strClass.substring('[object '.length);
    strClass = strClass.substring(0, strClass.length - 1).toLowerCase();

    if ('object' === strClass) {
        strClass = obj.constructor.name.toLowerCase();
    }

    let strObj = '';
    try {
        strObj = JSON.stringify(obj);
    } catch (ex) {
        strObj = obj.toString();
    }
    console.log(`[strClassName] ${strObj}`);
    console.log(`---- ${strClass}`);

    return strClass;
}

class Test {
    constructor() {}
}

let test = new Test();

// 引用类型
console.log(`测试引用类型`);
const arrObject = [
    { foo: 'bar' },
    [1, 2, 3, 4, 5],
    () => {
        console.log('hello world');
    },
    new Date(),
    new RegExp(/d+/),
    new Set([1, 1, 2, 2, 3, 3]),
    new WeakMap(),
    new Map(),
    test,
];

const basicObject = [
    null,
    undefined,
    false,
    'test',
    999,
    BigInt(9007199254740991),
    Symbol('foo'),
];

arrObject.forEach(toStringCall);
basicObject.forEach(toStringCall);

// basicObject.forEach(strClassName);
// arrObject.forEach(strClassName);

/** 测试结果 */
/**
测试引用类型
[toStringCall] {"foo":"bar"}
---- toString.call [object Object]
---- typeof object
---- class Object
[toStringCall] [1,2,3,4,5]
---- toString.call [object Array]
---- typeof object
---- class Array
[toStringCall] () => { console.log('hello world'); }
---- toString.call [object Function]
---- typeof function
---- class Function
[toStringCall] "2024-02-29T01:20:38.398Z"
---- toString.call [object Date]
---- typeof object
---- class Date
[toStringCall] {}
---- toString.call [object RegExp]
---- typeof object
---- class RegExp
[toStringCall] {}
---- toString.call [object Set]
---- typeof object
---- class Set
[toStringCall] {}
---- toString.call [object WeakMap]
---- typeof object
---- class WeakMap
[toStringCall] {}
---- toString.call [object Map]
---- typeof object
---- class Map
[toStringCall] {}
---- toString.call [object Object]
---- typeof object
---- class deepCopy
测试基础类型
[toStringCall] null
---- toString.call [object Null]
---- typeof object
---- class no name
[toStringCall] undefined
---- toString.call [object Undefined]
---- typeof undefined
---- class no name
[toStringCall] false
---- toString.call [object Boolean]
---- typeof boolean
---- class Boolean
[toStringCall] "test"
---- toString.call [object String]
---- typeof string
---- class String
[toStringCall] 999
---- toString.call [object Number]
---- typeof number
---- class Number
[toStringCall] 9007199254740991
---- toString.call [object BigInt]
---- typeof bigint
---- class BigInt
[toStringCall] undefined
---- toString.call [object Symbol]
---- typeof symbol
---- class Symbol
 */
